package net.servers
{
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.TimerEvent;
	import flash.net.Socket;
	import flash.utils.ByteArray;
	import flash.utils.setTimeout;
	
	import global.Managers;
	
	import net.translate.MessageTranslater;
	import net.translate.TranslateVO;
	
	import utils.EnterFrameTimer;
	import utils.encoding.Base64;
	import utils.pool.ObjectPool;
	
	public class ServerManager extends EventDispatcher
	{
		private static const SEND_INTERVAL		: Number = 0.2;		// 发送消息的最短时间间隔，以秒为单位
		private static const RECONNECT_TIME		: Number = 5;		// 断开重连的时间间隔，以秒为单位
		
		private var _callbackMap					: Object = {};
		private var _socket						: Socket;
		private var _messageQueue					: Array = [];
		private var _timer							: EnterFrameTimer;
		private var _bytes							: ByteArray = new ByteArray();
		
		private var _connected						: Boolean = false;
		
		public var ip								: String;
		public var port							: int;
		
		public function get connected():Boolean {
			return _connected;
		}
		
		public function ServerManager(ip:String = null, port:int = 0):void {
			this.ip = ip;
			this.port = port;
		}
		public function init():void {
			_timer = new EnterFrameTimer(SEND_INTERVAL * 1000);
			_timer.addEventListener(TimerEvent.TIMER, timerHandler);
			_timer.start();
			
			_socket = new Socket();
			_socket.addEventListener(Event.CONNECT, connectHandler);
			_socket.addEventListener(IOErrorEvent.IO_ERROR, errorHandler);
			_socket.addEventListener(Event.CLOSE, errorHandler);
			_socket.addEventListener(ProgressEvent.SOCKET_DATA, getDataHandler);
		}
		private function timerHandler(event:TimerEvent):void {
			if(this.connected) {
				if(_messageQueue.length > 0) {
					var obj:Object = _messageQueue.shift();
					var bytes:ByteArray = MessageTranslater.encode(obj.type, obj.data);
					_socket.writeBytes(bytes, 0, bytes.length);
					_socket.flush();
				}
			}
		}
		private function connectHandler(event:Event):void {
			_connected = true;
			dispatchEvent(event);
		}
		private function errorHandler(event:Event):void {
			_connected = false;
			_messageQueue.splice(0, _messageQueue.length);
			setTimeout(function():void {
				connect();
			}, RECONNECT_TIME * 1000);
		}
		private function getDataHandler(event:ProgressEvent):void {
			var value:TranslateVO;
			_socket.readBytes(_bytes, _bytes.length, _socket.bytesAvailable);
			var len:int = _bytes.readInt();
			if(_bytes.length >= len) {
				var tempBytes:ByteArray = ObjectPool.pop(ByteArray);
				_bytes.readBytes(tempBytes, 0, _bytes.bytesAvailable);
				_bytes.length = 0;
				_bytes.position = 0;
				tempBytes.position = len;
				tempBytes.readBytes(_bytes, 0, tempBytes.bytesAvailable);
				tempBytes.length = len;
				value = MessageTranslater.decode(tempBytes);
				var callbacks:Array = _callbackMap[value.name];
				if(callbacks != null) {
					for each(var callback:Function in callbacks) {
						callback(value.data);
					}
				}
				ObjectPool.push(tempBytes);
			}
		}
		
		public function connect(ip:String = null, port:int = 0):void {
			if(ip != null) this.ip = ip;
			if(port != 0) this.port = port;
			// 判断IP合法性
			if(!Managers.ipManager.isIPv4(this.ip)) return;
			// 判断端口合法性
			if(this.port < 1024 || this.port > 65535) return;
			_socket.connect(this.ip, this.port);
		}
		public function disconnect():void {
			_socket.close();
			_connected = false;
		}
		public function subscribe(type:String, callback:Function):void {
			var callbacks:Array = _callbackMap[type];
			if(callbacks == null) {
				callbacks = [];
				_callbackMap[type] = callbacks;
			}
			if(callbacks.indexOf(callback) < 0) {
				callbacks.push(callback);
			}
		}
		public function unsubscribe(type:String, callback:Function):void {
			var callbacks:Array = _callbackMap[type];
			if(callbacks != null) {
				var index:int = callbacks.indexOf(callback);
				if(index >= 0) {
					callbacks.splice(index, 1);
				}
			}
		}
		public function send(type:String, data:Object = null):void {
			_messageQueue.push({type:type, data:data});
		}
	}
}